/*
* EP-Tracing System : a tool to trace mountain hike.           .kNNx
* Copyright (C) 2009  Raffaello Brondi                         'KNNNk
*                                                             ,XNNNl
* This program is free software:                             ,XNN0'
* you can redistribute it and/or modify                     ,XNNx.
* it under the terms of the                                'XNNl
* GNU General Public License                              .KNN:                     ..
* as published by                                        .0NNo                    .ONNO.
* the Free Software Foundation,                          xNNN.  :dkd,            .KNNNN;
* either version 3 of the License, or                   :NNNK ;XNNNNNo          ,KNNNX:
* (at your option) any later version.                   xNNNNcXNNNNNNk         cNNNNx.
*                                                       ,XNNNNNNNNNNX'       .xNNNK;
*                                                        .lKNNNNNNNK'       :KNNNo
*                                                          ;kNNNNNKxl;'.'l0NNNk.
* This program is distributed in                          .'  .:xXNNNNNNNNNNNO,
* the hope that it will be useful,                       :NNXd'   ,oONNNNNNk,
* but WITHOUT ANY WARRANTY;                              xNNNNN0c.    .;;,.
* without even the implied warranty of                   kNNNNNNNXd'
* MERCHANTABILITY or                                     xNNNNNNNNNNOc.
* FITNESS FOR A PARTICULAR PURPOSE.                      xNNNNNNNNNNNNXd.
* See the GNU General Public License                     ONNNN0.,o0NNNNNNl
* for more details.                                      0NNNNd    kNNNNNX.
*                                                        KNNNNl   oNNNNNXc
*                                                       .XNNNN:  dNNNNNx.
* You should have received a copy                       .XNNNN; lNNNNO'
* of the GNU General Public License                     .XNNNN; 'dxo'
* along with this program.                              .XNNNN;
* If not, see <http://www.gnu.org/licenses/>.            0NNNN:
*                                                        kNNNNc
*                                                        oNNNNd
*                                                        ;NNNN0
* Contact: rbrondi@gmail.com                             .XNNNN;
*                                                         kNNNNd
*                                                         .coo:.
*/

package EP.GPS.Path;

import EP.FileSystemManager;
import java.util.Vector;

/**
 *
 * @author Raffaello Brondi
 */
public class PathConsumer extends Thread {

    private final byte LOOK_FORWARD = 6;
    private final float TOLLERANCE = 3.0F;
    private SwappablePathBuffer pb;

    public PathConsumer(SwappablePathBuffer pb) {
        this.pb = pb;
    }

    public void run() {
        System.out.println("\npath consumer start \n");

        Vector simplified = this.spatialSimplification();
        /*for (int i = 0; i < simplified.size(); i++) {
            System.out.println(simplified.elementAt(i));
        }*/
        System.out.println("\n Simplified " + (this.pb.size() - simplified.size()) + " of " + this.pb.size());
        StringBuffer stb = new StringBuffer();

        for (int i = 0; i < simplified.size(); i++) {
            stb.append((PathPoint) simplified.elementAt(i));
            if (i < simplified.size() - 1) {
                stb.append(",");
            }
        }
        FileSystemManager.getInstance().print(stb.toString());
        this.pb.swapFinished();
        System.out.println("path consumer end");
        return;
    }

    private Vector spatialSimplification() {
        Vector pp = new Vector();

        byte jump = (byte) Math.min(this.LOOK_FORWARD, this.pb.size());

        int start = 0;
        int end = this.LOOK_FORWARD;

        Line line;
        int i = 0;
        boolean flag;
        System.out.println("\n START SIMPLIFICATION form " + start + " to " + this.pb.size());
        while (start < this.pb.size()) {
            System.out.println("\n simplifing from " + start + " to " + end);
            if (end >= this.pb.size()) {
                end = this.pb.size() - 1;
            }
            if(start == this.pb.size() - 1){
                pp.addElement(this.pb.elementAt(start));
                break;
            }
            else if (end == start + 1) {
                pp.addElement(this.pb.elementAt(start));
                pp.addElement(this.pb.elementAt(end));
                start = end + 1;
                end = start + jump;
            } else {
                line = new Line(this.pb.elementAt(start), this.pb.elementAt(end));
                System.out.println("The line : " + line);
                i = end - 1;
                flag = false;

                //finds the first point between start and end that have a distance from the line greater than the accepted tollerance if exists.
                while (i > start && (flag = line.distance(this.pb.elementAt(i)) < this.TOLLERANCE)) {
                    i--;
                }
                
                if (flag) {
                    pp.addElement(this.pb.elementAt(start));
                    pp.addElement(this.pb.elementAt(end));
                    start = end + 1;
                    end = start + jump;
                } else {
                    end = i;
                }
            }
        }
        return pp;
    }

    /**
     * A Line in the form: aX + bY + c = 0;
     */
    private class Line {

        private double a;
        private double b;
        private double c;

        /**
         * Constructs the line that pass from the two point p1(x1, y1) and p2(x2, y2);
         */
        public Line(PathPoint p1, PathPoint p2) {
            this.a = p1.getY() - p2.getY();
            this.b = p2.getX() - p1.getX();
            this.c = (p1.getX() * (p2.getY() - p1.getY())) - (p1.getY() * (p2.getX() - p1.getX()));
        }

        /**
         * Returns the distance of the point p(x, y) from the line;
         * @param p the point.
         * @return the distanze between p and the line.
         */
        public double distance(PathPoint p) {
            double numeratore = Math.abs((this.a * p.getX()) + (this.b * p.getY()) + this.c);
            double denominatore = Math.sqrt(((this.a * this.a) + (this.b * this.b)));
            System.out.println("distance : " + (numeratore / denominatore));
            return numeratore / denominatore;
        }

        public String toString() {
            return this.a + "X + " + this.b + "Y + " + this.c + " = 0";
        }
    }
}
